package com.sdsy.blockchaintrainingback.service.docker;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.CreateNetworkResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.RemoteApiVersion;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.mysql.cj.log.Log;
import com.sdsy.blockchaintrainingback.config.properties.DockerProperties;
import com.sdsy.blockchaintrainingback.vo.training.CreateVncContainerVo;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.net.ServerSocket;
import java.time.Duration;
import java.util.*;

import static com.sdsy.blockchaintrainingback.utils.RandomStringGenerator.getRandomString;

/**
 * @Description TODO
 * @Author linzili
 * @Date 2023/9/27 08:28
 * @Version 1.0
 */

@Service
public class DockerService {
    private static final int MIN_PORT = 30000;
    private static final int MAX_PORT = 50000;
    private DockerClient dockerClient;
    @Resource
    private DockerProperties dockerProperties;

    @PostConstruct
    public void init() {


        DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(dockerProperties.getHost()).withApiVersion(RemoteApiVersion.VERSION_1_40).build();
        DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(config.getDockerHost())
                .maxConnections(100)
                .connectionTimeout(Duration.ofSeconds(30))
                .responseTimeout(Duration.ofSeconds(45))
                .build();
        this.dockerClient = DockerClientBuilder.getInstance(config).withDockerHttpClient(dockerHttpClient).build();
    }


    public List<Container> getContainerList() {
        return dockerClient.listContainersCmd().withShowAll(true).exec();
    }

    public void stopContainer(String containerId) {
        dockerClient.stopContainerCmd(containerId).exec();
    }

    public void startContainer(String containerId) {
        dockerClient.startContainerCmd(containerId).exec();
    }


    public int findNextAvailablePortForDocker() {

        Set<Integer> usedPorts = new HashSet<>();

        // Get all containers including the stopped ones
        List<Container> containers = dockerClient.listContainersCmd().withShowAll(true).exec();
        for (Container container : containers) {
            for (ContainerPort containerPort : container.getPorts()) {
                if (containerPort.getPublicPort() != null) {
                    usedPorts.add(containerPort.getPublicPort());
                }
            }
        }

        for (int port = MIN_PORT; port <= MAX_PORT; port++) {
            if (!usedPorts.contains(port) && isPortAvailable(port)) {
                return port;
            }
        }

        throw new RuntimeException("Failed to find an available port within the specified range.");
    }

    public boolean isPortAvailable(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            serverSocket.setReuseAddress(true);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public CreateVncContainerVo createVncContainer(String imageName,String networkId) {
        int availablePort = findNextAvailablePortForDocker();
        ExposedPort noVncPort = ExposedPort.tcp(6080);
        ExposedPort VncPort = ExposedPort.tcp(5900);
        Volume volume = new Volume("/root/Desktop/共享文件夹");
        Ports portBindings = new Ports();
        portBindings.bind(noVncPort, Ports.Binding.bindPort(availablePort));
        String password = getRandomString(6);
        CreateContainerResponse container = dockerClient.createContainerCmd(imageName)
                .withExposedPorts(noVncPort, VncPort)
                .withEnv("VNC_PASSWORD=" + password, "GEOMETRY=1920x1080", "DEPTH=24")
                .withVolumes(volume)
                .withHostConfig(new HostConfig().withNetworkMode(networkId).withPortBindings(portBindings).withBinds(new Bind("/root/data",volume)))
                .exec();

        dockerClient.startContainerCmd(container.getId()).exec();
        CreateVncContainerVo vo = new CreateVncContainerVo();
        vo.setContainerId(container.getId());
        vo.setContainerPort(availablePort);
        vo.setContainerVncPassword(password);
        return vo;
    }

    public String createNetwork(String name, String subnet) {
        Network.Ipam ipam = new Network.Ipam().withConfig(new Network.Ipam.Config().withSubnet(subnet));
        CreateNetworkResponse network = dockerClient.createNetworkCmd()
                .withName(name)
                .withIpam(ipam)
                .exec();
        return network.getId();
    }

    public void restartContainer(String containerId) {
        dockerClient.restartContainerCmd(containerId).exec();
    }

    @Async
    public void removeContainer(String containerId) {
        dockerClient.stopContainerCmd(containerId).exec();
        dockerClient.removeContainerCmd(containerId).exec();
    }
}
